package fojta;

public class ResistorPoint {
	
	final byte MAX_CONNECTIONS = 4;

	private ResistorPoint[] points = new ResistorPoint[MAX_CONNECTIONS];
	private Resistor[]   resistors = new Resistor[MAX_CONNECTIONS];
	
	private        int id               = 0;
	private static int lastId           = 0;

	static final int MAX_COUNT = 1000;
	
	// TODO rewrite constant 1000 to dynamic self-enlarged field
	public static ResistorPoint[] field =
		new ResistorPoint[MAX_COUNT];

	/**
	 * Simple constructor
	 */
	ResistorPoint() {
		id = nextId();
		field[id] = this;
	}

	private static int nextId() {
		return ++lastId;
	}
	
	/**
	 * returns Resistor id as String
	 */
	public String toString() {
		return String.valueOf(this.id);
	}
	
	/**
	 * Print resistor with each his neighbors
	 */
	public void print() {
		System.out.print( this + "( up="
				+ this.getUp() + ", right="
				+ this.getRight() + ", down="
				+ this.getDown() + ", left="
				+ this.getLeft() + "),"
		);
	}

	/**
	 * Each resistor is memorized through static array
	 * ResistorPoint.field[]
	 */
	public static void printAll() {
		for(int i = 0; i <= lastId; i++) {
			if( field[i] != null ) {
				field[i].print();
				System.out.print(" ");
			}
		}
	}

	/**
	 * Print all with new line at end
	 */
	public static void printlnAll() {
		printAll();
		System.out.println();
	}
	
	/**
	 * Remove all resistors
	 */
	public static void clearAll() {
		ResistorPoint tmp = null;
		for(int i = 0; i <= lastId; i++) {
			if( field[i] != null ) {
				tmp = field[i];
				tmp.finalize();
				tmp   = null;
				field[i] = null;
			}
		}
		lastId = 0;
	}	
	

	/**
	 * @param d
	 * @return
	 */
	public ResistorPoint getDir(Directions d) {
		return points[d.getId()]; 
	}
	
	public ResistorPoint getDir(byte index) {
		if( index >= MAX_CONNECTIONS ) {
			return null;
		}
		return points[index];
	}
	
	public Resistor getDirResistor(byte index) {
		if( index >= MAX_CONNECTIONS ) {
			return null;
		}
		return resistors[index];
	}
	
	private Resistor _setDir(Directions d, ResistorPoint rpt) {
		points[d.getId()] = rpt;
		if( rpt != null ) {
			Resistor resistor = new Resistor(this, rpt);
			resistors[d.getId()] = resistor;
			return resistor;
		}
		else {
			return null;
		}
	}

	public void setDir(Directions d, ResistorPoint rpt) {
		resistors[d.getId()] = rpt._setDir(d.opposite(),this);
		points[d.getId()] = rpt;
	}
	
	public void makeDir(Directions d) {
		this.setDir(d, new ResistorPoint());
	}
	
	/*
	 * Resistor getters
	 */
	public Resistor getLeftResistor() {
		return resistors[Directions.Left.getId()];
	}
	
	public Resistor getRightResistor() {
		return resistors[Directions.Right.getId()];
	}
	
	public Resistor getDownResistor() {
		return resistors[Directions.Down.getId()];
	}
	
	public Resistor getUpResistor() {
		return resistors[Directions.Up.getId()];
	}

	/*
	 * ResistorPoint getters
	 */
	public ResistorPoint getUp() {
		return getDir(Directions.Up);
	}
	
	public ResistorPoint getDown() {
		return getDir(Directions.Down);
	}
	
	public ResistorPoint getRight() {
		return getDir(Directions.Right);
	}
	
	public ResistorPoint getLeft() {
		return getDir(Directions.Left);
	}
	
	/*
	 * ResistorPoint setters
	 */
	public void setLeft(ResistorPoint left) {
		setDir(Directions.Left, left);
	}
	
	public void setRight(ResistorPoint right) {
		setDir(Directions.Right, right);
	}
	
	public void setDown(ResistorPoint rpt) {
		setDir(Directions.Down, rpt);
	}
	
	public void setUp(ResistorPoint up) {
		setDir(Directions.Up, up);
	}
	
	/*
	 * New ResistorPoint makers
	 */
	
	public ResistorPoint makeRight() {
		this.setRight(new ResistorPoint());
		return this.getRight();
	}
	
	public ResistorPoint makeLeft() {
		this.setLeft(new ResistorPoint());
		return this.getLeft();
	}	
	
	public ResistorPoint makeDown() {
		this.setDown(new ResistorPoint());
		return this.getDown();
		
	}
	
	public ResistorPoint makeUp() {
		this.setUp(new ResistorPoint());
		return this.getUp();
	}
	
	/*
	 * Core routines
	 */
	
	public int getId() {
		return id;
	}
	
	public void finalize()	{
		field[this.id] = null;
		
		ResistorPoint tmp;
		
		for(Directions d: Directions.values()) {
			tmp = this.getDir(d);
			if( tmp != null ) {
				tmp._setDir(d.opposite(), null);
				this._setDir(d, null);
			}
			tmp = null;
		}
	}
	
	/*
	 * Resistance
	 */

	public float getResistance() {
		return 0;
	}
	
	public float getResistance(Directions d) {
		return getResistance(d.getId());
	}
	
	public float getResistance(byte b) {
		if( b < MAX_CONNECTIONS) {
			Resistor R = resistors[b];
			if( R != null ) {
				return R.getResistance();
			}
			return 0;
		}
		return 0;
	}
	
	/*
	 * Connection computers
	 */
	
	public byte connectionCount() {
		byte count = 0;
		for(byte i = 0; i < MAX_CONNECTIONS; i++) {
			if( this.getDir(i) != null ) {
				count ++;
			}
		}
		return count;
	}
	
	/**
	 * go through all connected {@link ResistorPoint}
	 * @return not null {@link ResistorPoint}s
	 */
	public ResistorPoint[] allResistorPoints() {
		ResistorPoint[] rps = new ResistorPoint[connectionCount()];
		byte b = 0;
		for(byte i = 0; i < MAX_CONNECTIONS; i++) {
			if( this.getDir(i) != null ) {
				rps[b++] = this.getDir(i);
			}
		}
		return rps;
	}
	
	public Resistor[] allResistors() {
		Resistor[] rs = new Resistor[connectionCount()];
		byte b = 0;
		for(byte i = 0; i < MAX_CONNECTIONS; i++) {
			if( this.getDir(i) != null ) {
				rs[b++] = this.getDirResistor(i);
			}
		}
		return rs;
	}
	
	public void reduce() {
		if(connectionCount() == 2) {
			
			// get linked resistors
			Resistor[]       rs = allResistors();
			ResistorPoint[] rps = allResistorPoints();
			
			// and compute new resistance
			float newResistance = rs[0].getResistance() + rs[1].getResistance();
			// create new resistor with proper points and resistance
			Resistor newResistor = new Resistor(rps[0], rps[1]);
			newResistor.setResistance(newResistance);
			
			int index0 = rps[0].getIndexByPoint(this);
			int index1 = rps[1].getIndexByPoint(this);
			
			// reconnect points to new resistor
			if( index0 >= 0 && index1 >= 0) {
				rps[0].points[index0] = rps[1];
				rps[1].points[index1] = rps[0];
				rps[0].resistors[index0] = newResistor;
				rps[1].resistors[index1] = newResistor;
			}
			
		}
	}
	
	public int getIndexByPoint( ResistorPoint rpt ) {
		for(byte i = 0; i < MAX_CONNECTIONS; i++) {
			if( getDir(i) == rpt ) {
				return i; 
			}
		}
		return -1;
	}
	
	public int checkMultipleConnections () {
		ResistorPoint[] rps = allResistorPoints();
		for( int i = 0; i<= rps.length-1; i++) {
			int checkId = rps[i].getId();
			for( int j = i+1; j <= rps.length; j++) {
				if( checkId == rps[j].getId() ) {
					return checkId; 
				}
			}
		}
		return -1;
	}
}
